[Amazon SageMaker] イメージ分類のモデルをNeoで最適化して、RasPi4+OpenCV+Webカメラで使用してみました
1 はじめに
CX事業本部の平内(SIN)です。
Amazon SageMager Neo(以下、Neo)を使用すると、既存のモデルを簡単にRaspberryPiで動作させる事ができます。
[Amazon SageMaker] イメージ分類のモデルをNeoで最適化してRaspberryPi Model 4で使用してみました
今回、DLRを利用する部分は、上記と同じですが、カメラをWebカメラに変更して、OpenCVの画像を入力として扱う要領を試してみました。
はじめに動作している様子です。
2 入力データ
Amazon SageMakerのイメージ分類(組み込みアルゴリズム)の入力となる、{"data":[1,3,224,224]} の形式にOpenCVの画像データを使用するためには、以下の点に考慮が必要です。
(1) 正方形
カメラから取得できる画像のサイズは、カメラの出力に依存しますが、通常、これは長方形になっていると思います。 このため、まず、正方形に変形(トリミング)する必要があります。
下記のコードでは、フレームが横長である場合、高さを1辺の長さとした正方形にトリミングしています。
img = img[0 : int(height), 0 : int(height)]
(2) サイズ
サイズを244*244に変更する場合、resize() が利用可能です。正方形以外の画像で、この変換を行うと、画像が変形してしまうので注意が必要です。
img = cv2.resize(img, dsize=(224, 224))
(3) RGB
色相チャンネルのRBGの順は、期待されるもの違います。このため、下記のように変換が必要です。
# BGR => RGB img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
(4) ndarray
モデルの入力は、numpy.ndarrayです。OpenCVの画像データは、元々、numpy.ndarrayになっているので、この意味では変換の必要はありませんが、期待される、shapeが、(チャンネル数、列、行)であるため、この順番を入れ替える必要がります。
# (244,244,3) => (3,244,244) img = img.transpose((2, 0, 1))
3 コード
使用したすべてのコードは以下のとおりです。推論に時間を要するため、2秒に1回だけ推論して、結果表示を更新しています。具体的には、カメラのフレームレートを5fpsに設定し、10フレームに1回だけ推論処理しています。
import numpy as np from dlr import DLRModel from PIL import Image import cv2 # Webカメラ DEVICE_ID = 0 WIDTH = 800 HEIGHT = 600 FPS = 5 # Model CLASSES = ['PORIPPY(GREEN)', 'OREO', 'CUNTRY_MAM', 'PORIPPY(RED)', 'BANANA', 'CHEDDER_CHEESE', 'PRETZEL(YELLOW)', 'FURUGURA(BROWN)', 'NOIR', 'PRIME', 'CRATZ(RED)', 'CRATZ(GREEN)', 'PRETZEL(BLACK)', 'CRATZ(ORANGE)', 'ASPARA', 'FURUGURA(RED)', 'PRETZEL(GREEN)'] model_path = './models' class Model: def __init__(self, model_path): self.__model = DLRModel(model_path, 'cpu') def inference(self, image): out = self.__model.run({'data': image}) return out def putText(frame, x, y, text): font_size = 2 font_color = (255, 255, 255) cv2.putText(frame, text, (x, y), cv2.FONT_HERSHEY_SIMPLEX, font_size, font_color, 3, cv2.LINE_AA) return frame def decode_fourcc(v): v = int(v) return "".join([chr((v >> 8 * i) & 0xFF) for i in range(4)]) def main(): # Model初期化 model = Model(model_path) # カメラ初期化 cap = cv2.VideoCapture (DEVICE_ID) # フォーマット・解像度・FPSの設定 cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('Y','U','Y','V')) cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT) cap.set(cv2.CAP_PROP_FPS, FPS) # フォーマット・解像度・FPSの取得 fourcc = decode_fourcc(cap.get(cv2.CAP_PROP_FOURCC)) width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) fps = cap.get(cv2.CAP_PROP_FPS) print("fourcc:{} fps:{} width:{} height:{}".format(fourcc, fps, width, height)) # 推論は2秒に1回実行する counter = 0 interval = 2 name = "" probability = "" while True: # カメラ画像取得 _, frame = cap.read() if(frame is None): continue frame = frame[0 : int(height), 0 : int(height)] # => 正方形 counter += 1 if(counter % (fps * interval) == 0): # ModelのInput用に画像変換 image = cv2.resize(frame, dsize=(224, 224)) # => 244*244 image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) # RGB => BGR image = image.transpose((2, 0, 1)) # transpose shape (244,244,3)=>(3,244,244) # 推論 out = model.inference(image) print(out) name = CLASSES[np.argmax(out[0])] probability = "{:.5f}".format(np.max(out)) print("name:{} probability:{}".format(name,probability)) # 結果(テキスト)表示 frame = putText(frame, 10, int(height) - 100, name) frame = putText(frame, 10, int(height) - 30, probability) # 画像表示 cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # VideoCaptureオブジェクト破棄 cap.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
4 最後に
今回は、Neoで生成したモデルをOpenCVで取得した画像で試してみました。
SageMakerのエンドポイントと違い、入力データの形式を自前で整合させる必要があるので、少し、手間がかかると思います。